﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Px = PhysX;
using PxM = PhysX.Math;

namespace physxXNA
{
    public class ParticleEmitter
    {
        private PxM.Vector3 position;
        private PxM.Vector3 velocity;
        private int maxParticles;

        private Px.ParticleSystem particleSystem;
        public Px.ParticleSystem ParticleSystem
        {
            get { return particleSystem; }
            set { particleSystem = value; }
        }

        //num of new particles on 1 sec
        private float emmisionRate = 1;
        public float EmmisionRate
        {
            get { return emmisionRate; }
            set { emmisionRate = value; }
        }

        private float randPosMaxVal = 1;
        public float RandPosMaxVal
        {
            get { return randPosMaxVal; }
            set { randPosMaxVal = value; }
        }

        private float randVelMaxVal = 1;
        public float RandVelMaxVal
        {
            get { return randVelMaxVal; }
            set { randVelMaxVal = value; }
        }

        private TimeSpan lifeTime = new TimeSpan(0, 0, 1);
        public TimeSpan LifeTime
        {
            get { return lifeTime; }
            set { lifeTime = value; }
        }

        private PxM.Vector3[] pPositons;
        private PxM.Vector3[] pVelocities;
        private int[] pIndexes;
        private bool[] allocatedIndexes;
        private TimeSpan[] pLifeTimes;
        private int numParticles;

        private Random rand;

        public ParticleEmitter(Px.Physics physics, Px.Scene scene, PxM.Vector3 pos, PxM.Vector3 vel, int maxParticles)
        {
            position = pos;
            velocity = vel;
            this.maxParticles = maxParticles;
            rand = new Random();

            pPositons = new PxM.Vector3[maxParticles];
            pVelocities = new PxM.Vector3[maxParticles];
            pIndexes = new int[maxParticles];
            pLifeTimes = new TimeSpan[maxParticles];
            allocatedIndexes = new bool[maxParticles];
            numParticles = 0;

            for (int i = 0; i < maxParticles; i++)
            {
                pIndexes[i] = i;
                allocatedIndexes[i] = false;
                pLifeTimes[i] = lifeTime;
            }

                CreateParticleSystem(physics, scene);
        }

        #region Creation
        
        private void CreateParticleSystem(Px.Physics p, Px.Scene s)
        {
            particleSystem = p.CreateParticleSystem(maxParticles);

            particleSystem.SetVelocities(pVelocities, pIndexes);

            s.AddActor(particleSystem);
        }

        /*public void CreateParticles()
        {
            Px.Math.Vector3[] particlePositions = new Px.Math.Vector3[maxParticles];
            Px.Math.Vector3[] particleSpeed = new PxM.Vector3[maxParticles];
            int[] indexBuffer = new int[maxParticles];

            for (int i = 0; i < maxParticles; i++)
            {
                // Compute the location in a grid of particles + move the grid back so its center is at 0, 0, 0
                float x = (float)rand.NextDouble() * randPosMaxVal;
                float y = (float)rand.NextDouble() * randPosMaxVal;
                float z = (float)rand.NextDouble() * randPosMaxVal;

                particlePositions[i] = position + new Px.Math.Vector3(x, y, z);

                indexBuffer[i] = i;
                
            }

            for (int j = 0; j < maxParticles; j++)
            {
                float x = (float)rand.NextDouble() * randVelMaxVal;
                float y = (float)rand.NextDouble() * randVelMaxVal;
                float z = (float)rand.NextDouble() * randVelMaxVal;

                particleSpeed[j] = velocity + new PxM.Vector3(x, y, z);
            }

            Px.ParticleCreationData data = new Px.ParticleCreationData();
            data.NumberOfParticles = maxParticles;
            data.PositionBuffer = particlePositions;
            data.VelocityBuffer = particleSpeed;
            data.IndexBuffer = indexBuffer;
            

            particleSystem.CreateParticles(data);
        }*/

        public void SpawnParticle()
        {
            Px.Math.Vector3[] particlePositions = new Px.Math.Vector3[1];
            Px.Math.Vector3[] particleSpeed = new PxM.Vector3[1];
            int[] indexBuffer = new int[1];

            int index = FindProperIndex();
            if (index == -1)
                return;
            
            // Compute the location in a grid of particles + move the grid back so its center is at 0, 0, 0
            float x = (float)rand.NextDouble() * randPosMaxVal;
            float y = (float)rand.NextDouble() * randPosMaxVal;
            float z = (float)rand.NextDouble() * randPosMaxVal;

            particlePositions[0] = position + new Px.Math.Vector3(x, y, z);
            indexBuffer[0] = index;
            allocatedIndexes[index] = true;
            
            x = (float)rand.NextDouble() * randVelMaxVal;
            y = (float)rand.NextDouble() * randVelMaxVal;
            z = (float)rand.NextDouble() * randVelMaxVal;

            particleSpeed[0] = velocity + new PxM.Vector3(x, y, z);
            

            Px.ParticleCreationData data = new Px.ParticleCreationData();
            data.NumberOfParticles = 1;
            data.PositionBuffer = particlePositions;
            data.VelocityBuffer = particleSpeed;
            data.IndexBuffer = indexBuffer;

            particleSystem.CreateParticles(data);

            numParticles++;
        }

        private int FindProperIndex()
        {
            for (int i = 0; i < maxParticles; i++)
            {
                if (allocatedIndexes[i] == false)
                    return i;
            }
            return -1;
        }

        #endregion

        #region Update

        private TimeSpan time;

        public void Update(GameTime gameTime)
        {
            List<uint> particlesToRemove = new List<uint>();



            //Update existig particles;
            if (numParticles > 0)
            {
                Px.ParticleReadData pData = particleSystem.LockParticleReadData();
                if (pData != null)
                {
                    PxM.Vector3[] positions = pData.GetPositionBuffer();
                    Px.ParticleFlag[] flags = pData.GetFlagsBuffer();

                    for (uint i = 0; i < pData.ValidParticleRange; i++)
                    {   
                        pPositons[i] = positions[i];
                    }
                }
                pData.Unlock();
                

                /*if (particlesToRemove.Count > 0)
                {
                    uint[] pr = particlesToRemove.ToArray();
                    particleSystem.ReleaseParticles(pr.Length, pr);
                    numParticles -= particlesToRemove.Count;
                }*/

                
                time += gameTime.ElapsedGameTime;

                float p = emmisionRate * (float)time.TotalSeconds;
                int numParticlesToSpawn = (int)p;

                if (numParticlesToSpawn > 0)
                {
                    time = TimeSpan.Zero;//-= numParticlesToSpawn / emmisionRate;

                    for (int i = 0; i < numParticlesToSpawn && numParticles < maxParticles; i++)
                    {
                        SpawnParticle();
                    }
                }
            }           
        }

        #endregion
    }
}
